package com.itheima.sfbx.insurance.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.insurance.pojo.CategorySafeguard;
import com.itheima.sfbx.insurance.pojo.PlanSafeguard;
import com.itheima.sfbx.insurance.pojo.Safeguard;
import com.itheima.sfbx.insurance.mapper.SafeguardMapper;
import com.itheima.sfbx.insurance.service.ICategorySafeguardService;
import com.itheima.sfbx.insurance.service.IPlanSafeguardService;
import com.itheima.sfbx.insurance.service.ISafeguardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.transaction.annotation.Transactional;
import com.itheima.sfbx.insurance.constant.SafeguardCacheConstant;
import com.itheima.sfbx.insurance.dto.SafeguardVO;
import com.itheima.sfbx.insurance.enums.SafeguardEnum;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.google.common.collect.Lists;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import lombok.extern.slf4j.Slf4j;
/**
 * @Description：保障项服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SafeguardServiceImpl extends ServiceImpl<SafeguardMapper, Safeguard> implements ISafeguardService {

    private final ICategorySafeguardService categorySafeguardService;

    private final IPlanSafeguardService planSafeguardService;

    private LambdaQueryWrapper<Safeguard> queryWrapper(SafeguardVO safeguardVO){
        LambdaQueryWrapper<Safeguard> lqw = new LambdaQueryWrapper<>();
        lqw.eq(StrUtil.isNotBlank(safeguardVO.getSafeguardKey()),Safeguard::getSafeguardKey,safeguardVO.getSafeguardKey());
        lqw.like(StrUtil.isNotBlank(safeguardVO.getSafeguardKeyName()),Safeguard::getSafeguardKeyName,safeguardVO.getSafeguardKeyName());
        lqw.eq(StrUtil.isNotBlank(safeguardVO.getDataState()),Safeguard::getDataState,safeguardVO.getDataState());
        return lqw;
    }

    @Override
    @Cacheable(value = SafeguardCacheConstant.PAGE,key = "#pageNum+'-'+#pageSize+'-'+#safeguardVO.hashCode()")//缓存的key的设计一定要结合方法的所有参数
    //根据注解里的key和value，把这个方法的返回值存储到缓存中，如果下次来访问，能命中缓存就直接返回缓存的数据，反之如果没命中，同时把这个结果给存储到缓存中
    public Page<SafeguardVO> findPage(SafeguardVO safeguardVO, int pageNum, int pageSize) {
        //知道一件事情，是要分页查询一个表
        //通过对比前端和表结构，我们发现这个分页查询不需要查询别的表
        //SQL:select * from tab_safeguard where safeguard_key = ? and safeguard_key_name like ? and date_state = ?
        //两种方式
        LambdaQueryWrapper<Safeguard> lqw = queryWrapper(safeguardVO);
        Page<Safeguard> page = this.page(new Page<>(pageNum, pageSize), lqw);
        Page<SafeguardVO> voPage = BeanConv.toPage(page, SafeguardVO.class);
        return voPage;
    }

    @Override
    public SafeguardVO findById(String safeguardId) {
        //TODO
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Caching(put = @CachePut(value = SafeguardCacheConstant.BASIC,key = "#result.id"),
        evict = {
            @CacheEvict(value = SafeguardCacheConstant.PAGE,allEntries = true),
                @CacheEvict(value = SafeguardCacheConstant.LIST,allEntries = true),
        }
    )//allEntries指的是这个头下面的所有缓存
    public SafeguardVO save(SafeguardVO safeguardVO) {
        //1.经过前端对比数据库，我们得到结论：保存需要的字段前端已经全部给了
        //2.key是不能重复的
        //list one count page
        Integer count = lambdaQuery()
                .eq(Safeguard::getSafeguardKey, safeguardVO.getSafeguardKey())
                .count();
        if(count>0){
            //说明key已经存在了
            throw new ProjectException(SafeguardEnum.SAVE_FAIL);
        }
        Safeguard safeguard = BeanConv.toBean(safeguardVO, Safeguard.class);
        this.save(safeguard);
        return BeanConv.toBean(safeguard,SafeguardVO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Caching(
            evict = {
                    @CacheEvict(value = SafeguardCacheConstant.PAGE,allEntries = true),
                    @CacheEvict(value = SafeguardCacheConstant.LIST,allEntries = true),
                    @CacheEvict(value = SafeguardCacheConstant.BASIC,key = "#safeguardVO.id")
            }
    )
    public Boolean update(SafeguardVO safeguardVO) {
        //1.先查询
        Safeguard old = this.getById(safeguardVO.getId());
        if(old==null){
            //说明原数据不存在
            log.error("修改保障项失败，因为数据不存在：{}",safeguardVO.getId());
            throw new ProjectException(SafeguardEnum.UPDATE_FAIL);
        }
        //2.判断关联关系
        //2.1判断key
        //sql：select * from table key = ? and id != ?
        Integer count = lambdaQuery()
                .eq(Safeguard::getSafeguardKey, safeguardVO.getSafeguardKey())
                .ne(Safeguard::getId,safeguardVO.getId())//不等于
                .count();
        if(count>0){
            //说明key已经存在了
            throw new ProjectException(SafeguardEnum.UPDATE_FAIL);
        }

        //通过safeguardKey去查
        //sql：select count(*) from category_safeguard where safeguard_key = ? and date_state = 0
        Integer count1 = categorySafeguardService.lambdaQuery()
                .eq(CategorySafeguard::getSafeguardKey, safeguardVO.getSafeguardKey())
                .eq(CategorySafeguard::getDataState, SuperConstant.DATA_STATE_0)
                .count();
        Integer count2 = planSafeguardService.lambdaQuery()
                .eq(PlanSafeguard::getSafeguardKey, safeguardVO.getSafeguardKey())
                .eq(PlanSafeguard::getDataState, SuperConstant.DATA_STATE_0)
                .count();
        if(count1>0||count2>0){
            //不让修改
            log.error("修改保障项失败，因为存在其他关联关系：{}",safeguardVO.getId());
            throw new ProjectException(SafeguardEnum.UPDATE_FAIL);
        }
        //3.可以修改
        Safeguard safeguard = BeanConv.toBean(safeguardVO, Safeguard.class);
        safeguard.setUpdateBy(null);
        safeguard.setUpdateTime(null);
        return this.updateById(safeguard);
    }

    //什么时候需要加Transactional
    //第一种，多次操作（增删改）数据库
    //第二种，操作一次，但是操作完之后还有其他代码
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Caching(
            evict = {
                    @CacheEvict(value = SafeguardCacheConstant.PAGE,allEntries = true),
                    @CacheEvict(value = SafeguardCacheConstant.LIST,allEntries = true),
                    @CacheEvict(value = SafeguardCacheConstant.BASIC,allEntries = true)
            }
    )
    public Boolean delete(String[] checkedIds) {
        //1.把string类型转换成id的类型
        if(checkedIds==null||checkedIds.length==0){
            //说明没有传id
            return true;
        }
        List<Long> ids = new ArrayList<>();
        for (String checkedId : checkedIds) {
            ids.add(Long.parseLong(checkedId));
        }
        //先查出来
        List<Safeguard> safeguards = this.listByIds(ids);
        if (CollUtil.isEmpty(safeguards)) {
            return true;
        }
        Set<String> keySet = safeguards.stream().map(Safeguard::getSafeguardKey).collect(Collectors.toSet());
        Integer count1 = categorySafeguardService.lambdaQuery()
                .in(CategorySafeguard::getSafeguardKey, keySet)
                .eq(CategorySafeguard::getDataState, SuperConstant.DATA_STATE_0)
                .count();
        Integer count2 = planSafeguardService.lambdaQuery()
                .in(PlanSafeguard::getSafeguardKey, keySet)
                .eq(PlanSafeguard::getDataState, SuperConstant.DATA_STATE_0)
                .count();
        if(count1>0||count2>0){
            //不让修改
            log.error("删除保障项失败，因为存在其他关联关系：{}",keySet);
            throw new ProjectException(SafeguardEnum.DEL_FAIL);
        }
        return this.removeByIds(ids);
    }

    @Override
    @Cacheable(value = SafeguardCacheConstant.LIST,key = "#safeguardVO.hashCode()")
    public List<SafeguardVO> findList(SafeguardVO safeguardVO) {
        LambdaQueryWrapper<Safeguard> lqw = queryWrapper(safeguardVO);
        List<Safeguard> list = this.list(lqw);
        return BeanConv.toBeanList(list,SafeguardVO.class);
    }


    @Override
    public List<SafeguardVO> findShowPageItemByKey(List<String> safeguardKeyList) {
        //TODO
        return null;
    }

    @Override
    @Cacheable(value = SafeguardCacheConstant.BASIC,key = "#safeguardKey")
    public SafeguardVO findBySafeguardKey(String safeguardKey) {
        Safeguard one = lambdaQuery()
                .eq(Safeguard::getSafeguardKey, safeguardKey)
                .eq(Safeguard::getDataState, SuperConstant.DATA_STATE_0)
                .one();
        return BeanConv.toBean(one,SafeguardVO.class);
    }
}
